package com.itheima.reggie.web.filter;

import com.alibaba.fastjson.JSON;
import com.itheima.reggie.entity.User;
import com.itheima.reggie.utils.ThreadLocalUtil;
import com.itheima.reggie.web.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 登录鉴权相关的Filter
 *
 * @Author Vsunks.v
 * @Date 2023/2/4 14:39
 * @Blog blog.sunxiaowei.net/996.mba
 * @Description: 登录鉴权相关的Filter
 */
@Slf4j
@WebFilter("/*")
public class LoginFilter implements Filter {


    // 创建路径匹配器对象，用于检查请求的路径
    private final AntPathMatcher ATM = new AntPathMatcher();

    // 定义字符传，接收配置文件中配置的直接放行路径的规则
    @Value("${excludePath}")
    private String excludePath;

    /**
     * 过滤器，在该方法中实现过滤和放行逻辑
     *
     * @param request  请求对象
     * @param response 响应对象
     * @param chain    过滤器链
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        // 准备工作，把request和response转为与http相关对象
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        // 1. 获取本次请求的URI
        String requestURI = req.getRequestURI();

        // System.out.println("requestURI = " + requestURI);


        // 2. 判断本次请求, 是否需要登录, 才可以访问（如果是拦截器，不需要该步骤）
        // 定义所有格式模板
        // String[] uris = {"/backend/**", "/front/**", "/favicon.ico", "/employee/login"};

        String[] uris = excludePath.split(",");
        if (
            // requestURI.startsWith("/backend")
            //         || requestURI.startsWith("/front")
            //         || "/favicon.ico".equals(requestURI)
            //         || "/employee/login".equals(requestURI)

                checkURI(uris, requestURI)
        ) {
            // 3. 如果不需要，则直接放行
            chain.doFilter(req, resp);
            // System.out.println("直接放行");
            // 如果已经放行，后面的校验逻辑就不需要进行了
            return;
        }

        // 4. 判断登录状态，如果已登录，则直接放行
        Object employeeId = req.getSession().getAttribute("employee");

        // 5. 如果未登录, 则返回未登录结果
        if (employeeId != null) {
            // 记录线程名称，用于验证同一个请求是否是一个线程
            // log.error("线程信息：{}",Thread.currentThread().getName());

            // 在放行之前设置当前登录用户的id，本次请求之后的Controller、service等方法处理中都可以获取该id
            ThreadLocalUtil.setCurrentId((Long) employeeId);

            // 已登录，则放行
            chain.doFilter(req, resp);
            // System.out.println("登陆后放行");
            // 如果已经放行，后面的校验逻辑就不需要进行了
            return;
        }


        // 4. 判断登录状态，如果已登录，则直接放行
        User user = (User) req.getSession().getAttribute("user");

        // 5. 如果未登录, 则返回未登录结果
        if (user != null) {
            // 记录线程名称，用于验证同一个请求是否是一个线程
            // log.error("线程信息：{}",Thread.currentThread().getName());

            // 在放行之前设置当前登录用户的id，本次请求之后的Controller、service等方法处理中都可以获取该id
            ThreadLocalUtil.setCurrentId(user.getId());

            // 已登录，则放行
            chain.doFilter(req, resp);
            // System.out.println("登陆后放行");
            // 如果已经放行，后面的校验逻辑就不需要进行了
            return;
        }

        log.info("未登陆访问");
        // 这里使用的是Servlet中原生的filter，不会经过SpringMVC框架的处理，所以需要手动序列化后响应
        // 相应的内容，必须是R中包裹一个字符串"NOTLOGIN"，前端页面写死的内容
        resp.getWriter().write(JSON.toJSONString(R.fail("NOTLOGIN")));

    }


    /**
     * 检查uri是否符合指定格式要求，符合返回true，否则返回false
     *
     * @param uris       指定的格式数组(多个格式组成的数组)
     * @param requestURI 被校验的字符串路径
     * @return 符合任意一个格式就返回true，都不符合返回false
     */
    private boolean checkURI(String[] uris, String requestURI) {

        for (String uriPattern : uris) {
            boolean result = ATM.match(uriPattern, requestURI);
            // 符合任意一个格式就返回true
            if (result) {
                return true;
            }
        }
        // 都不符合返回false
        return false;
    }
}
